package game;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JPanel;

public class JuegoCanvas extends JPanel implements MouseListener {

	private static final long serialVersionUID = 1L;
	private Celda[][] matrix;
	private Celda[] bombas;
	private Celda izq, der;
	public static final int FILAS = 8,
					  COLUMNAS = 5,
					  IMG_ANCHO =350,
					  IMG_ALTO = 500,
					  CELDADIM = 50;
					
	private int puntos;
	private boolean noSepuedeCrear;
	private Object lock = new Object();
	private boolean tieneAnimacion;
	
	public JuegoCanvas(){
		Celda[][] m =new Celda[FILAS][COLUMNAS];
		Celda[] arr = new Celda[COLUMNAS];
		for(int k = 0; k<arr.length; k++){ arr[k] = new Celda(true); }
		izq = new Celda(true);
		izq.setPosicion(new Point(0,FILAS*CELDADIM));
		izq.setAction(new Runnable() {
			@Override
			public void run() {
				moverIzq();
			}
		});
		der = new Celda(true);
		der.setPosicion(new Point(CELDADIM+CELDADIM*COLUMNAS, FILAS*CELDADIM));
		der.setAction(new Runnable() {
			@Override
			public void run() {
				moverDer();
			}
		});
		setBombas(arr);
		setMatrix(m);
		puntos = 0;
		noSepuedeCrear = false;
		tieneAnimacion = false;
		this.addMouseListener(this);
	}
	
	private void moverIzq(){
		synchronized (lock) {
			if(tieneAnimacion) return;
		}
	 	recorrerIzq();
	 	establecerCeldas(0, izq);
	}
	
	private void moverDer(){
		synchronized (lock) {
			if(tieneAnimacion) return;
		}
		recorrerDer();
	 	establecerCeldas(COLUMNAS-1, der);
	}
	
	private void disparar(final int index){
		
		synchronized (lock) {
			if(tieneAnimacion) return;
			tieneAnimacion = true;
		}
		final Celda celda = bombas[index];
		if(celda == null) synchronized (lock) {tieneAnimacion = false; return;}
		celda.setAction(null);
		
		new Thread(){
			public void run(){
				int pos = 0;
				for(; pos < FILAS-1; pos ++){
					if(matrix[pos][index] == null){
						break;
					}
				}
				
				while(true){
					if(celda.getPosicion().y > CELDADIM*pos){
						celda.getPosicion().y--;
						repaint();
						try {
							Thread.sleep(1);
						} catch (InterruptedException e) {}
					}else
						break;
				}
				analizarDisparo(celda, pos, index);
				repaint();
				bombas[index] = null;
				synchronized (lock) {
					tieneAnimacion = false;
				}
			}
		}.start();
	}
	
	private void analizarDisparo(Celda celda, int pos, int index){
		if(celda.getColor().equals(Color.black)){
			disparoEspecialColumna(celda, pos, index);
			return;
		}
		if(celda.getColor().equals(Color.white)){
			disparoEspecialFila(celda, pos, index);
			return;
		}
		if(pos>0){
			Celda celda2 = matrix[pos-1][index];
			if(celda2.getColor().equals(celda.getColor())){
				matrix[pos-1][index] = null;
				puntos++;
				return;
			}
		}
		matrix[pos][index] = celda;
	}
	
	private void disparoEspecialFila(Celda celda, int pos, int index) {
		for(int k = 0; k< COLUMNAS; k++){
			if(matrix[pos][k] != null) puntos ++;
			matrix[pos][k] = null;
		}
		animacionBloqueDestruido(pos);
		
	}

	private void animacionBloqueDestruido(int pos) {
		int cant = CELDADIM;
		while(cant > 0){
			boolean pausa = false;
			for(int k = pos; k< FILAS; k++){
				for(int j = 0; j<COLUMNAS; j++){
					if(matrix[k][j] != null){
						pausa = true;
						matrix[k][j].getPosicion().y--;
					}
				}
			}
			cant --;
			if(pausa){
				repaint();
				try {Thread.sleep(5);
				} catch (InterruptedException e) {}
			}else break;
		}
		for(int k = pos+1; k<FILAS; k++)
			for(int j = 0; j<COLUMNAS;j++){
				matrix[k-1][j] = matrix[k][j]; 
			}
		repaint();
	}

	private void disparoEspecialColumna(Celda celda, int pos, int index) {
		for(int k = 0; k<FILAS; k++){
			if(matrix[k][index] != null) puntos++;
			matrix[k][index] = null;
		}
		
	}

	private void establecerCeldas(int index, Celda ref){
		bombas[index] = new Celda(false);
	 	bombas[index].setColor(ref.getColor());
	 	setBombaCellPosition(bombas[index], index);
	 	ref.setColor();
	 	repaint();
	}
	private void setBombaCellPosition(Celda celda,final int index) {
		celda.setPosicion(new Point(CELDADIM+CELDADIM*index, FILAS*CELDADIM));
		celda.setAction(new Runnable() {
			@Override
			public void run() {
				disparar(index);
			}
		});
	}
	
	private void recorrerIzq() {
		int ini = 0;
		
		int fin = ini;
		for(int i = fin;i < bombas.length; i++){
			fin = i;
			if(bombas[i] == null){
				break;
			}
		}
		
		Celda[] bombas2 = new Celda[COLUMNAS];
		int len = fin-ini+1>= bombas.length? bombas.length-1: fin-ini+1;
		System.arraycopy(bombas, ini, bombas2, ini +1,len);
		System.arraycopy(bombas, fin+1, bombas2, fin+1, bombas.length-fin-1);
		setBombas(bombas2);
	}

	private void recorrerDer() {
		int ini = 0;
		
		int fin = bombas.length-1;
		for(; fin >= 0; fin--){
			if(bombas[fin] == null){
				break;
			}
		}
		
		Celda[] bombas2 = new Celda[COLUMNAS];
		int ini2 = ini;
		if(fin == bombas.length-1 && bombas[fin] != null){
			ini++;
		}
		if(fin < 0) {
			fin++;
		}
		System.arraycopy(bombas, fin+1, bombas2, fin, bombas.length-fin-1);
		System.arraycopy(bombas, ini, bombas2, ini2, fin);
		setBombas(bombas2);
	}


	public Celda[][] getMatrix() {
		return matrix;
	}
	
	public void setMatrix(Celda[][] matrix) {
		this.matrix = matrix;
	}
	
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		Image i = this.createImage(IMG_ANCHO, IMG_ALTO);
		this.paintJuego(i.getGraphics());
		Dimension d = this.getSize();
		g.drawImage(i, 0, 0, d.width, d.height, this);
	}

	private void paintJuego(Graphics g) {
		dibujarMatrix(g);
		dibujarCeldas(g, bombas, true);
		dibujarSiguientes(g);
		dibujarScore(g);
	}
	private void dibujarScore(Graphics g) {
		g.drawString("Puntos: "+ puntos, 10, CELDADIM*(FILAS+1)+20);
	}

	private void dibujarSiguientes(Graphics g) {
		dibujarCelda(g, izq);
		dibujarCelda(g, der);
	}

	private void dibujarCeldas(Graphics g, Celda[] celdas, boolean conTexto){
		int k = 1;
		
		g.setFont(new Font(g.getFont().getName(), Font.PLAIN, 20));
		for(Celda celda : celdas){
			dibujarCelda(g, celda);
			if(celda != null && conTexto){
				if(celda.getColor().equals(Color.black)) g.setColor(Color.white);
				else g.setColor(Color.black);
				g.drawString(k+"", celda.getPosicion().x+20, celda.getPosicion().y+30);
			}
			k++;
			
		}
	}

	private void dibujarCelda(Graphics g, Celda celda) {
		if(celda != null){
			g.setColor(celda.getColor());
			g.fillRect(celda.getPosicion().x, 
					   celda.getPosicion().y,
					   CELDADIM, CELDADIM);
			g.setColor(Color.black);
			g.drawRect(celda.getPosicion().x, 
					   celda.getPosicion().y,
					   CELDADIM, CELDADIM);
		}
	}

	private void dibujarMatrix(Graphics g) {
		for(Celda[] arr : matrix){
			dibujarCeldas(g, arr, false);
		}
	}

	public Celda[] getBombas() {
		return bombas;
	}

	public void setBombas(Celda[] bombas) {
		this.bombas = bombas;
		for(int k = 0; k<bombas.length; k++){
			if(bombas[k] != null){
				setBombaCellPosition(bombas[k], k);
			}
		}
	}

	public void crearNuevasCeldas() {
		if(!ultimaFilaNull()){
			noSepuedeCrear = true;
			return;
		}
		moverCeldarParaArriba();
		crearNuevasCeldasEnPrimeraFila();
		actualizarPosiciones();
		
		repaint();
	}

	private void actualizarPosiciones() {
		for(int k = 0; k<COLUMNAS; k++){
			for(int j = 0; j<FILAS; j++ ){
				if(matrix[j][k] == null) continue;
				matrix[j][k].setPosicion(new Point(CELDADIM+k*CELDADIM,j*CELDADIM));
			}
		}
	}

	private void crearNuevasCeldasEnPrimeraFila() {
		for(int k = 0; k<COLUMNAS; k++){
			matrix[0][k] = new Celda(false);
		}
	}

	private void moverCeldarParaArriba() {
		Celda[][] matriz = new Celda[FILAS][COLUMNAS];
		System.arraycopy(matrix, 0, matriz, 1, FILAS-1);
		matrix = matriz;
	}

	private boolean ultimaFilaNull() {
		for(int k = 0; k < COLUMNAS; k++){
			if(matrix[FILAS-1][k] != null)
				return false;
		}
		return true;
	}

	public boolean hemosPerdido() {
		return noSepuedeCrear;
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {}
	@Override
	public void mouseClicked(MouseEvent arg0) {}

	@Override
	public void mouseEntered(MouseEvent arg0) {}

	@Override
	public void mouseExited(MouseEvent arg0) {}
	

	@Override
	public void mousePressed(MouseEvent ev) {
		if(hemosPerdido()) return;
		
		Celda celda = buscarCelda(ev);
		
		if(celda != null){
			
			celda.perform(ev);
		}
	}

	private Celda buscarCelda(MouseEvent ev) {
		Celda res = null;
		Point p = normalizar(ev.getPoint());
		for(Celda celda : bombas){
			if(celda != null){
				res = celdaContienePunto(celda, p);
				if(res != null) break;
			}
		}
		if(res == null) res = celdaContienePunto(izq, p);
		if(res == null) res = celdaContienePunto(der, p);
		return res;
	}
	private Point normalizar(Point point) {
		
		Dimension d = this.getSize();
		return new Point(point.x * IMG_ANCHO/ d.width, point.y*IMG_ALTO / d.height);
	}

	private Celda celdaContienePunto(Celda celda, Point p){
		Point cp = celda.getPosicion();
		if(cp.x <= p.x && p.x <= cp.x + CELDADIM && 
		   cp.y <= p.y && p.y <= cp.y + CELDADIM)
			return celda;
		return null;
	}

	public Object lock() {
		
		return lock ;
	}

	public boolean tieneAnimacion() {
		
		return tieneAnimacion;
	}

	public int getPuntos() {
		return puntos;
	}
	public void reconocerVoz(String palabra){
		if(palabra.equals("one")) disparar(0);
		else if(palabra.equals("two")) disparar(1);
		else if(palabra.equals("three")) disparar(2);
		else if(palabra.equals("four")) disparar(3);
		else if(palabra.equals("five")) disparar(4);
		else if(palabra.equals("left")) moverIzq();
		else if(palabra.equals("right")) moverDer();
	}
	
}
